initial import boxroom 0.6.2
[boxroom-stian.git] / vendor / plugins / rubyzip-0.9.1 / lib / quiz1 / t / solutions / Jim Menard / solitaire_cypher.rb
bloba8a3bdad9c7a982c88dd0ac00c350993d3f76a08
1 #! /usr/bin/env ruby
3 RANKS = %w(A 2 3 4 5 6 7 8 9 10 J Q K)
4 SUITS = %w(C D H S)
5 JOKER_RANK = 'joker'
6 JOKER_VALUE = -1
8 class Card
9     def Card.value_to_chr(value)
10         i = value
11         i -= 26 while i > 26
12         (i + ?A - 1).chr
13     end
14     def Card.chr_to_value(chr)
15         i = chr[0] - ?A + 1
16         i += 26 while i < 0
17         i
18     end
20     def initialize(rank, suit)
21         @rank = rank
22         @suit = suit
23         if rank == JOKER_RANK
24             @value = JOKER_VALUE
25         else
26             @value = (SUITS.index(suit) * 13) + RANKS.index(rank) + 1
27         end
28     end
30     def to_s
31 #       return @value.to_s if @value != JOKER_VALUE
32 #       return @suit.to_s
33         "#{@rank}#{@suit} #{@value.to_s}"
34     end
36     def to_i
37         @value
38     end
40     def chr
41         Card.value_to_chr(@value)
42     end
43 end
45 class Deck
47     def initialize
48         @cards = []
49         SUITS.each { | suit |
50             RANKS.each { | rank | @cards << Card.new(rank, suit) }
51         }
52         @joker_a = Card.new(JOKER_RANK, 'A')
53         @cards << @joker_a
54         @joker_b = Card.new(JOKER_RANK, 'B')
55         @cards << @joker_b
56     end
58     # Keys the deck and returns itself.
59     def key
60         # do nothing; keyed when initialized
61         self
62     end
64     # Return the next kestream value as a number (not a string).
65     # Keep going until we have a non-joker value.
66     def next_keystream
67         val = JOKER_VALUE
68         until val != JOKER_VALUE
69             val = generate_next_keystream_value
70         end
71         val
72     end
74     # Return the next keystream value as a number 1-26 (not a string).
75     def generate_next_keystream_value
76         move(@joker_a, 1)
77         move(@joker_b, 2)
78         triple_cut()
79         count_cut()
80         return output_number()
81     end
83     # Move a card a certain distance. Wrap around the end of the deck.
84     def move(card, distance)
85         old_pos = @cards.index(card)
86         new_pos = old_pos + distance
87         new_pos -= (@cards.length-1) if new_pos >= @cards.length
88         @cards[old_pos,1] = []
89         @cards[new_pos,0] = [card]
90     end
92     # Perform a triple cut around the two jokers. All cards above the top
93     # joker move to below the bottom joker and vice versa. The jokers and the
94     # cards between them do not move.
95     def triple_cut
96         i = @cards.index(@joker_a)
97         j = @cards.index(@joker_b)
98         j, i = i, j if j < i    # make sure i < j
99         @cards = slice(j+1, -1) + slice(i, j) + slice(0, i-1)
100     end
102     # Perform a count cut using the value of the bottom card. Cut the bottom
103     # card's value in cards off the top of the deck and reinsert them just
104     # above the bottom card.
105     def count_cut
106         i = @cards[@cards.length - 1].to_i
107         @cards = slice(i, -2) + slice(0, i-1) + [@cards[@cards.length-1]]
108     end
110     # Returns a non-nil cut of cards from the deck.
111     def slice(from, to)
112         slice = @cards[from..to]
113         return slice || []
114     end
116     # Return the output number (not letter). Convert the top card to it's
117     # value and count down that many cards from the top of the deck, with the
118     # top card itself being card number one. Look at the card immediately
119     # after your count and convert it to a letter. This is the next letter in
120     # the keystream. If the output card is a joker, no letter is generated
121     # this sequence. This step does not alter the deck.
122     def output_number
123         i = @cards[0].to_i
124         i -= @cards.length if i >= @cards.length
125         num = @cards[i].to_i
126         num -= 26 if num > 26
127         num
128     end
130     def to_s
131         @cards.join(' ')
132     end
135 class CryptKeeper
137     def initialize(deck)
138         @keyed_deck = deck
139     end
141     def decrypt(str)
142         @deck = @keyed_deck.dup
143         answer = ""
144         str.split(//).each { | c |
145             if c == ' '
146                 answer << ' '
147                 next
148             end
150             msg_num = Card.chr_to_value(c)
151             key = @deck.next_keystream
152             diff = msg_num - key
153             diff += 26 if diff < 1
154             answer << Card.value_to_chr(diff)
155         }
156         answer
157     end
159     def encrypt(str)
160         @deck = @keyed_deck.dup
161         answer = ''
162         str.split(//).each { | c |
163             if c == ' '
164                 answer << ' '
165                 next
166             end
168             msg_num = Card.chr_to_value(c)
169             key = @deck.next_keystream
170             sum = msg_num + key
171             sum -= 26 if sum > 26
172             answer << Card.value_to_chr(sum)
173         }
174         answer
175     end
177     def crypto_each(str)
178         @deck = @keyed_deck.dup
179         str.split(//).each { | c | yield c }
180     end
184 def prep_arg(str)
185     str = str.upcase.gsub(/[^A-Z]/, '')
186     words = []
187     while str.length > 0
188         words << str[0...5]
189         str[0...5] = ''
190     end
192     last_len = words[words.length-1].length
193     words[words.length-1] += ('X' * (5 - last_len)) if last_len < 5
194     words.join(' ')
197 if __FILE__ == $0
198     if ARGV[0]
199         puts CryptKeeper.new(Deck.new.key).decrypt(prep_arg(ARGV[0]))
200     else
201         puts CryptKeeper.new(Deck.new.key).decrypt('CLEPK HHNIY CFPWH FDFEH')
202         puts CryptKeeper.new(Deck.new.key).decrypt('ABVAW LWZSY OORYK DUPVH')
203     end